package com.pronhub.cloud.shop.service.impl;

import com.pronhub.cloud.shop.domain.Good;
import com.pronhub.cloud.shop.domain.SeckillGood;
import com.pronhub.cloud.shop.feign.GoodFeignApi;
import com.pronhub.cloud.shop.mapper.SeckillGoodMapper;
import com.pronhub.cloud.shop.mq.MQConstants;
import com.pronhub.cloud.shop.msg.SeckillCodeMsg;
import com.pronhub.cloud.shop.redis.MyRedisTemplate;
import com.pronhub.cloud.shop.redis.SeckillKeyPrefix;
import com.pronhub.cloud.shop.service.ISeckillGoodService;
import com.pronhub.cloud.shop.web.vo.SeckillGoodVo;
import com.pronhub.shop.cloud.BussinessException;
import com.pronhub.shop.cloud.Result;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;

@Service
public class SeckillGoodServiceImpl implements ISeckillGoodService {
    @Autowired
    private SeckillGoodMapper seckillGoodMapper;
    //远程调用
    @Autowired
    private GoodFeignApi goodFeignApi;

    @Autowired
    private MyRedisTemplate myRedisTemplate;

    @Autowired
    private RabbitTemplate rabbitTemplate;
    @Override
    public List<SeckillGoodVo> query() {
        //1单标查询相关的数据查询所有秒杀商品的数据
        List<SeckillGood> seckillGoodList = seckillGoodMapper.query();
        //如果有很多场秒杀商品 需要对商品的ID进行去重 {1,2,1}
        Set<Long> idSet = new HashSet<>();
        for (SeckillGood seckillGood : seckillGoodList) {
            idSet.add(seckillGood.getGoodId());//进行去重
        }
        //2根据查到的GoodId {1,2} 去调用远程方法去查出来相对应的数据
        List<Long> ids = new ArrayList<>(idSet);
        //3调用远程good-server的方法 传递Ids{1,2}去查t_good出来GoodList

        Result<List<Good>> result = goodFeignApi.query(ids);
        //result==null  ====>远程无法调用(比如网络问题，商品服务宕机)
        //result!=200 ===>远程服务可以调用，但在在内部调用过程中出现问题，统一异常处理了，返回Result(code=50020x)

        //如果不成功怎么办
        if (result.getCode()!=200 || result==null){
            //说明在远程调用服务内部出错了，统一异常处理返回来Result(code=50020x)
            throw new BussinessException(SeckillCodeMsg.PRODUCT_SERVER_ERROR);
        }

        List<Good> goodList = result.getData();

        //可以通过GoodId找到good对象.因为进行了相对应的封装,所以可以直接的找到
        Map<Long,Good> goodMap = new HashMap<>();
        for(Good good:goodList){
            goodMap.put(good.getId(),good);
        }
        //4聚合城SeckillGoodVo
        List<SeckillGoodVo> seckillGoodVoList = new ArrayList<>();
        //SeckillGoods有多少条数据SeckillGoodVo就有多少条数据
        for (SeckillGood sg : seckillGoodList) {
            Good good = goodMap.get(sg.getGoodId());

            SeckillGoodVo vo = new SeckillGoodVo();
            //把SeckillGood和Good的数据封装成SeckillVo
            vo.setGoodDetail(good.getGoodDetail());//商品详情信息
            vo.setGoodImg(good.getGoodImg());//商品图片
            vo.setGoodName(good.getGoodName());//商品名称
            vo.setGoodDetail(good.getGoodDetail());//商品详情
            vo.setGoodPrice(good.getGoodPrice());//商品原价
            vo.setGoodTitle(good.getGoodTitle());//商品标题
            vo.setEndDate(sg.getEndDate());//秒杀结束时间
            vo.setGoodId(good.getId());//商品id
            vo.setId(sg.getId());//秒杀场次Id
            vo.setSeckillPrice(sg.getSeckillPrice());//秒杀价格
            vo.setStartDate(sg.getStartDate());//秒杀开始时间
            vo.setStockCount(sg.getStockCount());//秒杀数量
            seckillGoodVoList.add(vo);

        }

        return seckillGoodVoList;
    }

    @Override
    public List<SeckillGoodVo> queryFormCache() {

        Map<String, SeckillGoodVo> stringSeckillGoodVoMap = myRedisTemplate.hgetAll(SeckillKeyPrefix.SECKILL_GOOD_HASH, "", SeckillGoodVo.class);
        return new ArrayList<>(stringSeckillGoodVoMap.values());
    }

    @Override
    public SeckillGoodVo findById(Long seckillId) {
        if(seckillId==null){
            return null;
        }
        SeckillGood sg = seckillGoodMapper.get(seckillId);
        if(sg==null){
            return null;
        }
        List<SeckillGood> seckillGoodList = new ArrayList<>();
        seckillGoodList.add(sg);
        List<SeckillGoodVo> seckillGoodVoList = getSeckillGoodVos(seckillGoodList);
        //System.err.println("233");
        return seckillGoodVoList.get(0);//获取第一个元素


    }

    @Override
    public SeckillGoodVo findByIdFromCache(Long seckillId) {
        SeckillGoodVo vo = myRedisTemplate.hget(SeckillKeyPrefix.SECKILL_GOOD_HASH, "", seckillId + "", SeckillGoodVo.class);
        return vo;
    }

    /**
     * 库存-1的服务
     * @param seckillId
     */
    @Override
    public int decrSeckillCountBySeckillId(Long seckillId) {
        return seckillGoodMapper.decrSeckillCountBySeckillId(seckillId);
    }

    @Override
    public void syncRedisStockCount(Long seckillId) {
        //Redis+1的回滚操作  为了保证一致性 我们先从MyBatis数据库查出来相对应的库存数量|
        int stockCount = seckillGoodMapper.getStockCount(seckillId);
        if (stockCount>0){
            //说明还有商品 取消本地的表示 还有进行Redis+1的操作
            rabbitTemplate.convertAndSend(MQConstants.SECKILL_OVER_SIGN_PUBSUB_EX,"",seckillId);
            //把库存商品进行同步操作
            myRedisTemplate.set(SeckillKeyPrefix.SECKILL_STOCK_COUNT,"",stockCount);
        }

    }

    @Override
    public void incrStock(Long seckillId) {
        seckillGoodMapper.incrStock(seckillId);
    }

    //下面是抽出来的(老师的版本)
    private List<SeckillGoodVo> getSeckillGoodVos(List<SeckillGood> seckillGoodList) {
        Set<Long> idSet = new HashSet<>();
        for(SeckillGood sg:seckillGoodList){
            idSet.add(sg.getGoodId());//把所有的goodId放入到set集合中，达到去重的目的
        }
        //2.获取到所有的good_id集合ids[1,2]
        List<Long> ids = new ArrayList<>(idSet);// [1,2]
        //3.调用good-server远程方法,传递参数ids[1,2],在商品服务中单表查询t_goods数据 GoodList
        Result<List<Good>> result = goodFeignApi.query(ids);
        //result==null  ====>远程无法调用(比如网络问题，商品服务宕机)
        //result.hasError() ===>远程服务可以调用，但在在内部调用过程中出现问题，统一异常处理了，返回Result(code=50020x)
        if(result.getCode()!=200 || result==null){
            //说明在远程调用服务内部出错了，统一异常处理返回来Result(code=50020x)
            throw new BussinessException(SeckillCodeMsg.PRODUCT_SERVER_ERROR);
        }
        //远程没有出错，如果远程调用有问题，result.getData()==null
        List<Good> goodList = result.getData(); //发起远程调用.TODO
        //可以通过GoodId找到good对象.
        Map<Long,Good> goodMap = new HashMap<>();
        for(Good good:goodList){
            goodMap.put(good.getId(),good);
        }
        //4.把SeckillGoodList和GoodList在内存中做聚合,然后把数据封装成List<SeckillGoodVo>
        List<SeckillGoodVo> seckillGoodVoList = new ArrayList<>();
        //SeckillGoodList有几条数据，seckillGoodVoList集合有相同数量的数据.
        for(SeckillGood sg:seckillGoodList){
            Good good = goodMap.get(sg.getGoodId());
            //把SeckillGood和Good的数据封装成SeckillVo
            SeckillGoodVo vo = new SeckillGoodVo();
            vo.setGoodDetail(good.getGoodDetail());//商品详情信息
            vo.setGoodImg(good.getGoodImg());//商品图片
            vo.setGoodName(good.getGoodName());//商品名称
            vo.setGoodDetail(good.getGoodDetail());//商品详情
            vo.setGoodPrice(good.getGoodPrice());//商品原价
            vo.setGoodTitle(good.getGoodTitle());//商品标题
            vo.setEndDate(sg.getEndDate());//秒杀结束时间
            vo.setGoodId(good.getId());//商品id
            vo.setId(sg.getId());//秒杀场次Id
            vo.setSeckillPrice(sg.getSeckillPrice());//秒杀价格
            vo.setStartDate(sg.getStartDate());//秒杀开始时间
            vo.setStockCount(sg.getStockCount());//秒杀数量
            seckillGoodVoList.add(vo);
        }
        return seckillGoodVoList;
    }
}
